#!/usr/bin/env python3
# -*-coding: utf-8 -*-

import binascii
import re
import functools

INITIAL_MODBUS = 0xFFFF
INITIAL_DF1 = 0x0000


class CRC_PACK(object):
    """docstring for CRC_PACK"""

    def __init__(self,):
        self.table = CRC_PACK.init_table()

    @classmethod
    def init_table(cls):
        # Initialize the CRC-16 table,
        #   build a 256-entry list, then convert to read-only tuple
        lst = []
        for i in range(256):
            data = i << 1
            crc = 0
            for j in range(8, 0, -1):
                data >>= 1
                if (data ^ crc) & 0x0001:
                    crc = (crc >> 1) ^ 0xA001
                else:
                    crc >>= 1
            lst.append(crc)
        table = tuple(lst)
        return table

    def calc_b_Str(self, b_st, crc=INITIAL_MODBUS):
        """Given a bunary string and starting CRC, Calc a final CRC-16 """
        for ch in b_st:
            crc = (crc >> 8) ^ self.table[(crc ^ ch) & 0xFF]
        return crc

    def crc_chreck(self, st):
        return 0 == self.calc_b_Str(st)

    @functools.lru_cache(maxsize=256)
    def gen_st_crc(self, a):
        aa = re.sub("[^0-9a-fA-F]", "", a)
        if len(aa) % 2 == 1:
            aa = aa + "0"
        b = binascii.a2b_hex(aa)
        c = self.calc_b_Str(b)
        cc = "{:02x}{:02x}".format(c % 256, c // 256)
        d = binascii.a2b_hex(cc)
        ddd = CRC_PACK.insert_space(aa + cc)
        return b + d, ddd, self.crc_chreck(b)

    @classmethod
    def insert_space(cls, st):
        """
        "123456" --> "12 34 56"
        "1234567" --> "12 34 56"
        """
        it = iter(st)
        return " ".join(a + b for a, b in zip(it, it))


cp = CRC_PACK()
cp.gen_st_crc("0123456")
